package dao;

import helper.DSNHelper;

import java.sql.ResultSet;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Properties;

import util.DBConfig;
import util.Debug;
/**
 * General database access class (Singleton).
 * @author Jolle
 * @version 1.0.0
 */
public class DBAccess implements DBAccessInterface {
	
	private static DBAccess instance = null;
	private DBAccessInterface dba = null;
	private DBTypeEnum connType = DBTypeEnum.NONE;
	private DSN dsn = null;
	private String dbName = "";

	/**
	 * Private constructor.
	 */
	private DBAccess() {
		
	}
	/**
	 * Return the current instance.
	 * @return instance
	 * @throws DBException
	 */
	public static DBAccess getInstance() throws DBException {
		if (instance == null) {
			instance = new DBAccess();
		}
		if (!instance.isConnected()) {
			instance.connect();
		}
		return instance;
	}
	/**
	 * Set the connection type.
	 * @param connType
	 */
	public void setConnType(DBTypeEnum connType) {
		this.connType = connType;
	}
	/**
	 * Set the DSN.
	 * @param dsn
	 */
	public void setDSN(DSN dsn) {
		this.dsn = dsn;
	}
	/**
	 * Get the database name.
	 * @return String
	 */
	public String getDbName() {
		return this.dbName;
	}
	/**
	 * Check whether a connection exists.
	 * @return true if a connection is open
	 */
	public boolean isConnected() {
		return (this.connType != DBTypeEnum.NONE);
	}
	/**
	 * Connect to database, related to configured connection type.
	 * @throws DBException
	 */
	public void connect() throws DBException {
		/* get database configuration object */
		DBConfig dbConfig = new DBConfig();
		/* read properties from configuration file */
		dbConfig.readConfiguration();
		/* get all properties from db configuration */
		Properties configProps = dbConfig.getConfiguration();
		/* get dsn from db configuration */
		this.setDSN(DSNHelper.getDSNfromProperties(configProps));
		/* get connection type from db configuration */
		this.connType = DBTypeEnum.valueOf(configProps.getProperty("type"));
		/* get db name from configuration properties */
		if (this.connType == DBTypeEnum.MYSQL) {
			this.dbName = configProps.getProperty("dbname");
		}
		else {
			this.dbName = configProps.getProperty("dbpath") + 
					configProps.getProperty("dbname");
		}
		/* check configuration */
		boolean configIsOK = dbConfig.isConfigOK(false);
		/* terminate db configuration panel */
		Debug.print("dispose dialog");
		dbConfig.dispose();
		if (!configIsOK) {
			throw new DBException("Database configuration erroneous");
		}
		connect(this.dbName);
	}
	/**
	 * Connect to database, related to configured connection type.
	 * @param database
	 * @throws DBException
	 */
	public void connect(String database) throws DBException {
		if (this.connType == DBTypeEnum.DERBY) {
			this.dba = AccessDerby.getInstance();
		}
		else if (this.connType == DBTypeEnum.ACCESS) {
			/* this works if using a user dsn */
			this.dba = AccessMSAcc.getInstance();
			/* alternatively use this part */
			/* ATTENTION! This is still in TEST STAGE! */
			//this.dba = AccessJacks.getInstance();
		}
		else {
			this.dba = AccessMySQL.getInstance();
		}
		this.dba.setDSN(this.dsn);
		this.dba.connect(database);
	}
	/**
	 * Execute an sql statement without return any value.
	 * @param query
	 * @throws DBException
	 */
	public void execute(String query) throws DBException {
		if (!this.isConnected()) {
			this.connect();
		}
		this.dba.execute(query);
	}
	/**
	 * Get a result set from a query.
	 * @param query
	 * @return ResultSet
	 * @throws DBException
	 */
	public ResultSet query(String query) throws DBException {
		if (!this.isConnected()) {
			this.connect();
		}
		return this.dba.query(query);
	}
	/**
	 * Close current statement.
	 */
	public void closeStatement() throws DBException {
		this.dba.closeStatement();
	}
	/**
	 * Close database connection.
	 * @throws DBException
	 */
	public void closeConnection() throws DBException {
		this.dba.closeConnection();
		this.connType = DBTypeEnum.NONE;
	}
	/**
	 * Get a list of existing databases in the current connection.
	 * @return String array with database names or schemas
	 */
	public String[] getDataBasesList() throws DBException {
		if (!this.isConnected()) {
			this.connect();
		}
		return this.dba.getDataBasesList();
	}
	/**
	 * Get a list of existing tables in the current database.
	 * @return String array with table names
	 */
	public String[] getTablesList() throws DBException {
		if (!this.isConnected()) {
			this.connect();
		}
		return this.dba.getTablesList();
	}
	/**
	 * Get a list of key/value pairs (field name/field value).
	 * @param query
	 * @return ArrayList
	 * @throws DBException
	 */
	public ArrayList<HashMap<String, Object>> getResultAsList(String query) throws DBException {
		if (!this.isConnected()) {
			this.connect();
		}
		return this.dba.getResultAsList(query);
	}
	/**
	 * Create the given table.
	 * @param name of the table to create
	 * @return SQL statement as string
	 * @throws DBException
	 */
	public void createTable(String table) throws DBException {
		this.dba.createTable(table);
	}
	/**
	 * Check whether the given table exists in current database.
	 * @param table name to check
	 * @return true if table exists, otherwise false
	 * @throws DBException
	 */
	public boolean tableExists(String table) throws DBException {
		if (!this.isConnected()) {
			this.connect();
		}
		return this.dba.tableExists(table);
	}
}
